/* ============================================================
 * Part of 'Autostop' project
 * Under GPL license - http://www.gnu.org/licenses/gpl.html
 * (c) 2006 - Autostop team
 */

///@file image.c Image manipulation functions

#include <assert.h>			// uses assert()
#include <stdlib.h>			// uses malloc/free

#ifdef __linux
#include <netinet/in.h>		// uses htonl, ntohl
#elif _WIN32
#include <winsock.h>
#endif

#include "analyzer.h"		// main defiitions


// ============================================================
/// Alocates image data and initializes pixels with zeroes.
/// Free created image with free_image().
///@param width		image width
///@param height	image height
///
///@return pointer to image object, or NULL on error
pimage_t create_image( int width, int height )
{
	assert( ( width >= 0 ) && ( height >= 0 ) );
	
	// allocate
	size_t needed_size = sizeof( image_t ) + sizeof( pixel_t ) * width * height;
	pimage_t pimage = (pimage_t) malloc( needed_size );
	
	// initialize
	assert ( pimage );
	
	pimage->width = width;
	pimage->height = height;

	foreach_pixel( p, pimage ) {
		*p = 0;
	} // TODO may use calloc instead, but wnated totest the macro :)

	return pimage;
}

// ============================================================
/// Alocates map data and initializes pixels with zeroes.
/// Free created map with free_map().
///@param width		map width
///@param height	map height
///
///@return pointer to map object, or NULL on error
pmap_t create_map( int width, int height )
{
	assert( ( width > 0 ) && ( height > 0 ) );
	
	// allocate
	size_t needed_size = sizeof( map_t ) + sizeof( int ) * width * height;
	pmap_t pmap = (pmap_t) calloc( needed_size, 1 );
	
	// initialize
	assert ( pmap );
	
	pmap->width = width;
	pmap->height = height;

	return pmap;
}


// ============================================================
/// Frees allocated image. Does nothing if pimage is NULL.
///@param pimage image to free. Must be valid pointer or NULL
void free_image( pimage_t pimage )
{
	free( pimage );
}

// ============================================================
/// Frees allocated map. Does nothing if pmap is NULL.
///@param pmap map to free. Must be valid pointer or NULL
void free_map( pmap_t pmap )
{
	free( pmap );
}

// ============================================================
/// Creates new image as copy of source image
/// Free created image with free_image().
///@param psource source image. Must be valid pointer
///
///@return pointer to image object, or NULL on error
pimage_t create_image_copy( const pimage_t psource )
{
	assert( psource );
	// allocate NOTE: not using create_image, as initilization is not needed
	size_t needed_size = sizeof( image_t ) + sizeof( pixel_t ) * psource->width * psource->height;
	pimage_t pimage = (pimage_t) malloc( needed_size );

	// copy data
	assert( pimage );
	
	pimage->width	= psource->width;
	pimage->height	= psource->height;

	pixel_t* pdst = pimage->pixels;
	foreach_pixel( psrc, psource ) {
		*pdst = *psrc;

		++ pdst;
	}

	return pimage;
}

// ===========================================================
/// Load image data from file and creates new image object with the data.
/// The file format is:
///	- width		- 4 bytes big endian
///	- height	- 4 bytes big endian
///	- data		- 1 byte per pixel, rows from top to bottom, left to right
///\par		stream input stream, must be valid
///\return	created map, or NULL pointer on error
pimage_t load_image( FILE* stream )
{
	assert( stream );

	uint32_t widthbig	= 0;	// width and height - big endian
	uint32_t heightbig	= 0;
	uint32_t width		= 0;	// width and height in local byte order
	uint32_t height		= 0;
	pimage_t pimage		= NULL;	// result image
	
	// get width and height in big-endian from stream

	if ( fread( & widthbig, 4, 1, stream ) != 1 )
	{
		return NULL;
	}
	if ( fread( & heightbig, 4, 1, stream ) != 1 )
	{
		return NULL;
	}

	// verify
	if ( widthbig == 0 || heightbig == 0 )
	{
		return NULL;
	}
	
	// convert big endian to local
	width = ntohl( widthbig );
	height = ntohl( heightbig );

	// create image
	pimage = create_image( width, height );

	// load data
	if ( pimage )
	{
		int bytes_to_read = width * height;
		if ( fread( pimage->pixels, bytes_to_read, 1, stream ) != 1 )
		{
			free_image( pimage );
			return NULL;
		}
	}

	// success! (or no, if pimage is NULL )
	return pimage;
	
}

// ==========================================================
/// Saves existing image into file.
///\see load_image for file format
///\par		stream	output stream, must be valid
///\par		pimage	image object, must be valid
///\return	0 if ok, non-zero on error
int save_image( FILE* stream, pimage_t pimage )
{
	assert( stream );
	assert( pimage );

	// get image size in big-endian
	uint32_t widthbig	= 0;
	uint32_t heightbig	= 0;
	int bytes_to_write	= 0;

	widthbig	= htonl( pimage->width );
	heightbig	= htonl( pimage->height );

	// store sizes in stream
	if ( fwrite( &widthbig, 4, 1, stream ) != 1 )
	{
		return 1;
	}
	if ( fwrite( &heightbig, 4, 1, stream ) != 1 )
	{
		return 2;
	}

	// store data in stream
	bytes_to_write = pimage->width * pimage->height;
	if ( fwrite( pimage->pixels, bytes_to_write, 1, stream ) != 1 )
	{
		return 3;
	}

	return 0; // success!
}

// eof 

